﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using Microsoft.Win32;

namespace LANmine
{
    public partial class PickupsForm : Form
    {
        private Language lmLang;
        private User lmUser;
        private ListViewGame SelectedGame;
        private Database roDb = new Database("ro");
        private Database rwDb = new Database("rw");
        private DateTime lastUpdate;
        private RegistryKey rKey = Registry.CurrentUser.OpenSubKey("Software\\" + Application.ProductName);

        /// <summary>
        /// Standard Constructor
        /// </summary>
        public PickupsForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Custom Constructor
        /// </summary>
        /// <param name="lmLang">LANmine Language Object</param>
        /// <param name="lmUser">LANmine User Object</param>
        public PickupsForm(Language lmLang, User lmUser)
            : this()
        {
            // Save objects
            this.lmLang = lmLang;
            this.lmUser = lmUser;

            // Dock the window
            this.Dock = DockStyle.Fill;

            // Apply language overrides
            ApplyLanguage();
        }

        #region Language Specifications

        /// <summary>
        /// Language Specification
        /// </summary>
        private void ApplyLanguage()
        {
            // Header Label
            this.lblPickups.Text = this.lmLang.GetString("menu.btnPickups");
        }

        #endregion

        #region Member Functions

        /// <summary>
        /// Load a list of games into the given ListView
        /// </summary>
        /// <param name="lvw">The ListView to fill up</param>
        private void LoadGames(ListView lvw)
        {
            // Temp Variables
            String gName;

            // Database Objects
            DataSet ds = new DataSet();
            DataTable dt;
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da;

            // DataAdapter
            da = new SqlDataAdapter("SELECT * FROM games", dbConn);

            // Make sure the games table's clear
            if (ds.Tables["games"] != null)
                ds.Tables["games"].Rows.Clear();

            // Fill the games table with incoming data
            da.Fill(ds, "games");

            // Extract the table from the DataSet
            dt = ds.Tables["games"];

            // Clear the ListView
            lvw.Clear();

            // Add group
            lvw.Groups.Add(new ListViewGroup("games", lmLang.GetString("pickups.lvwGames")));

            foreach (DataRow r in dt.Rows)
            {
                // Prepare Data
                gName = r["naam"].ToString();

                // Create ListViewGame based on the database row
                ListViewGame cItem = new ListViewGame(new string[] { r["naam"].ToString() });

                // Insert data into the ListViewGame
                cItem.ID = Convert.ToUInt32(r["id"].ToString());
                cItem.ShortName = r["short"].ToString();
                cItem.Name = gName;

                // Push the current item to the team game group
                lvw.Items.Add(cItem);

                // Assign group to the new game
                lvw.Items[gName].Group = lvw.Groups["games"];
            }
        }

        /// <summary>
        /// Load the latest x entries of a given game's chatlog
        /// </summary>
        /// <param name="gId">The ID of the game to filter</param>
        /// <param name="logAmount">The amount of chat entries to fetch</param>
        /// <param name="txt">The TextBox to manipulate</param>
        private void LoadLog(UInt32 gId, UInt16 logAmount, TextBox txt)
        {
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da = new SqlDataAdapter("lmGetChatLog", dbConn);
            DataSet ds = new DataSet();

            try
            {
                // Dealing with SProc
                da.SelectCommand.CommandType = CommandType.StoredProcedure;

                // Add Parameters
                da.SelectCommand.Parameters.Add("@GameId", SqlDbType.Int);
                da.SelectCommand.Parameters["@GameId"].Value = gId;

                da.SelectCommand.Parameters.Add("@LogLength", SqlDbType.Int);
                da.SelectCommand.Parameters["@LogLength"].Value = logAmount;

                // Create and fill the DataTable in the DataSet
                da.Fill(ds, "chat");

                // Create temp variables
                DateTime time;
                String user;
                String msg;
                String newContents = lmLang.GetString("pickups.lvwChat");

                // Insert game name into string
                newContents = String.Format(newContents, SelectedGame.Name.ToString());

                // Write the update time of this query
                if (ds.Tables["chat"].Rows.Count > 0)
                    this.lastUpdate = Convert.ToDateTime(ds.Tables["chat"].Rows[0]["utime"]);

                foreach (DataRow r in ds.Tables["chat"].Rows)
                {
                    // Extract data from query
                    time = Convert.ToDateTime(r["mtime"].ToString());
                    user = r["gebruikersnaam"].ToString();
                    msg = r["message"].ToString();

                    // Append new messages to temp variable
                    newContents += Environment.NewLine + "<" + time.ToLongTimeString() + "> " + "[ " + user + " ] " + msg;
                }

                // Replace the text by the temp variable
                txt.Text = newContents;

                // Scroll to bottom
                txt.ScrollToCaret();
            }
            catch (Exception ex)
            {
                MessageBox.Show(lmLang.GetString("pickups.errLoadChat") + ex.Message);
            }
        }

        /// <summary>
        /// Load a list of users that have posted a chat message over the past 5 minutes
        /// </summary>
        /// <param name="lvw">The ListView to manipulate</param>
        private void LoadActivePlayers(TimeSpan time, ListView lvw)
        {
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da = new SqlDataAdapter("lmGetActiveChat", dbConn);
            DataSet ds = new DataSet();

            try
            {
                // Stored Procedure
                da.SelectCommand.CommandType = CommandType.StoredProcedure;

                // Add parameters
                da.SelectCommand.Parameters.Add("@TimeStamp", SqlDbType.DateTime2);
                da.SelectCommand.Parameters["@TimeStamp"].Value = DateTime.UtcNow.Subtract(time);

                // Fill up the DataSet
                da.Fill(ds, "active");

                // Clear ListView
                lvw.Items.Clear();

                // Add group
                lvw.Groups.Add(new ListViewGroup("active", lmLang.GetString("pickups.lvwActive")));

                // Create temp variables
                String uName;

                // Check for empty result set
                if (ds.Tables["active"].Rows.Count == 0)
                {
                    // Add empty record
                    lvw.Items.Add(lmLang.GetString("pickups.errNoActive"));

                    // Assign group
                    lvw.Items[0].Group = lvw.Groups["active"];
                }

                foreach (DataRow r in ds.Tables["active"].Rows)
                {
                    // Store username
                    uName = r["gebruikersnaam"].ToString();

                    // Create ListViewItem
                    ListViewItem aUsr = new ListViewItem(uName);
                    aUsr.Name = uName;

                    // Add row to ListView
                    lvw.Items.Add(aUsr);

                    // Assign group to row
                    lvw.Items[uName].Group = lvw.Groups["active"];
                }
            }
            catch (Exception ex)
            {
                // Show error
                MessageBox.Show(lmLang.GetString("pickups.errGetActive") + "\n" + ex.Message);
                // Stop updating until the tab re-opens
                tmrActive.Stop();
            }
        }

        /// <summary>
        /// Gets all entries that have been posted after the specified DateTime
        /// </summary>
        /// <param name="gId">The ID of the game to filter</param>
        /// <param name="lastPoll">The date of the last update</param>
        /// <param name="txt">TextBox to manipulate</param>
        private void UpdateLog(UInt32 gId, DateTime lastPoll, TextBox txt)
        {
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da = new SqlDataAdapter("lmUpdateChatLog", dbConn);
            DataSet ds = new DataSet();

            try
            {
                // Dealing with SProc
                da.SelectCommand.CommandType = CommandType.StoredProcedure;

                // Add Parameters
                da.SelectCommand.Parameters.Add("@GameId", SqlDbType.Int);
                da.SelectCommand.Parameters["@GameId"].Value = gId;

                da.SelectCommand.Parameters.Add("@LastPoll", SqlDbType.DateTime2);
                da.SelectCommand.Parameters["@LastPoll"].Value = lastPoll;

                da.SelectCommand.Parameters.Add("@UserId", SqlDbType.Int);
                da.SelectCommand.Parameters["@UserId"].Value = this.lmUser.ID;

                // Create and fill the DataTable in the DataSet
                da.Fill(ds, "chat");

                // Create temp variables
                DateTime time;
                String user;
                String msg;

                // Write the update time of this query
                if (ds.Tables["chat"].Rows.Count > 0)
                    this.lastUpdate = Convert.ToDateTime(ds.Tables["chat"].Rows[0]["utime"]);

                foreach (DataRow r in ds.Tables["chat"].Rows)
                {
                    // Extract data from query
                    time = Convert.ToDateTime(r["mtime"].ToString());
                    user = r["gebruikersnaam"].ToString();
                    msg = r["message"].ToString();

                    // Append new messages to the textbox
                    txt.AppendText(Environment.NewLine + "<" + time.ToLongTimeString() + "> " + "[ " + user + " ] " + msg);
                    // Scroll to bottom
                    txt.ScrollToCaret();
                }
            }
            catch (Exception ex)
            {
                // Display error
                MessageBox.Show(lmLang.GetString("pickups.errUpdateChat") + ex.Message);
                // Stop updating
                tmrChat.Stop();
            }
        }

        /// <summary>
        /// Send a chat message to the active lobby and return the DateTime at which it was posted.
        /// </summary>
        /// <param name="uId">User ID of the sender</param>
        /// <param name="gId">Game ID of the selected game</param>
        /// <param name="txtMsg">Content of the message</param>
        /// <returns>Timestamp of when the post was made</returns>
        private DateTime SendChat(UInt32 uId, UInt32 gId, String txtMsg)
        {
            SqlConnection dbConn = this.rwDb.getDbConn();
            SqlCommand insCom = new SqlCommand("lmInsertChatMsg", dbConn);
            DateTime insTime = new DateTime();

            // Dealing with SProc
            insCom.CommandType = CommandType.StoredProcedure;

            // Add Parameters
            insCom.Parameters.Add("@UserId", SqlDbType.Int);
            insCom.Parameters["@UserId"].Value = uId;

            insCom.Parameters.Add("@GameId", SqlDbType.Int);
            insCom.Parameters["@GameId"].Value = gId;

            insCom.Parameters.Add("@ChatMsg", SqlDbType.VarChar, 1000);
            insCom.Parameters["@ChatMsg"].Value = txtMsg;

            // Return Parameter
            SqlParameter retVal = new SqlParameter("@InsTime", SqlDbType.DateTime2);
            retVal.Direction = ParameterDirection.Output;
            insCom.Parameters.Add(retVal);

            try
            {
                // Insert the new record
                dbConn.Open();
                insCom.ExecuteNonQuery();
                dbConn.Close();

                // Convert return value to DateTime
                insTime = Convert.ToDateTime(retVal.Value);

                // Return DateTime object
                return insTime;
            }
            catch (Exception ex)
            {
                dbConn.Close();
                MessageBox.Show(lmLang.GetString("pickups.errSendChat") + "\n" + ex.Message);
            }

            return insTime;
        }

        /// <summary>
        /// Scroll to the bottom of the chat log and focus the new message field.
        /// </summary>
        private void ScrollToBottom()
        {
            // Scroll to the bottom of the chatbox
            txtChat.Focus();
            txtChat.ScrollToCaret();
            txtChat.DeselectAll();

            // Re-focus the chat message
            txtMsg.Focus();
        }

        #endregion

        #region Event Handlers

        private void PickupsForm_Load(object sender, EventArgs e) 
        {
            // Load a list of games into lvwGames
            LoadGames(lvwGames);
            // Select the first item
            lvwGames.Items[0].Selected = true;
            // Get LogLines from registry
            UInt16 logLines = Convert.ToUInt16(rKey.GetValue("ChatLines", 100).ToString());

            // The first item should now be in SelectedGame

            // Load the log of the current game into txtChat
            // TODO: Amount of log entries set in registry
            LoadLog(this.SelectedGame.ID, logLines, txtChat);

            // 5 minute TimeSpan
            // TODO: Activity timespan set in registry
            TimeSpan time = new TimeSpan(0, 5, 0);

            // Load active players into the ListView
            LoadActivePlayers(time, lvwActive);
        }

        private void PickupsForm_ClientSizeChanged(object sender, EventArgs e)
        {
            // Resize the ListView tiles
            lvwGames.TileSize = new Size(lvwGames.ClientSize.Width, lvwGames.TileSize.Height);
            lvwActive.TileSize = new Size(lvwActive.ClientSize.Width, lvwActive.TileSize.Height);
        }

        private void lvwGames_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Make sure we're not overwriting the same object
            if (lvwGames.SelectedItems.Count > 0 && this.SelectedGame != (ListViewGame)lvwGames.SelectedItems[0])
            {
                // Put the selected game into the SelectedGame property
                this.SelectedGame = (ListViewGame)lvwGames.SelectedItems[0];
                // Load a log of chat messages for the selected game
                LoadLog(this.SelectedGame.ID, 100, txtChat);
                // Scroll to bottom
                ScrollToBottom();
            }
        }

        private void PickupsForm_Activated(object sender, EventArgs e)
        {
            // Scroll to the bottom of the chat log
            ScrollToBottom();

            // Start updating chat
            tmrChat.Start();
            tmrActive.Start();
        }

        private void PickupsForm_Deactivate(object sender, EventArgs e)
        {
            // Stop the update ticker
            tmrChat.Stop();
        }

        private void txtMsg_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && txtMsg.Text.Length > 0)
            {
                // Send the chat message and retrieve the insertion time
                DateTime insTime = SendChat(this.lmUser.ID, this.SelectedGame.ID, txtMsg.Text);

                // Add our message to the chat log
                txtChat.AppendText(Environment.NewLine + "<" + insTime.ToLongTimeString() + "> " + "[ " + this.lmUser.UserName + " ] " + txtMsg.Text);
                // Scroll to the bottom
                ScrollToBottom();
                // Empty the chat field
                txtMsg.Clear();

                // Update the Active list
                // TODO: from registry
                LoadActivePlayers(new TimeSpan(0, 5, 0), lvwActive);
            }
        }

        private void tmrChat_Tick(object sender, EventArgs e)
        {
            // Update the log with new messages
            UpdateLog(this.SelectedGame.ID, this.lastUpdate, this.txtChat);
        }

        private void tmrActive_Tick(object sender, EventArgs e)
        {
            // Get TimeSpan
            Int32 spanMin = Convert.ToInt32(rKey.GetValue("TimeActiveUsers", 5).ToString());

            // 5 minute TimeSpan
            TimeSpan time = new TimeSpan(0, spanMin, 0);

            // Load active players into the ListView
            LoadActivePlayers(time, lvwActive);
        }

        #endregion
    }
}